from typing import List, Union
from sympy import (
    S,
    I,
    Abs,
    Eq,
    Reals,
    Function,
    Symbol,
    nonlinsolve,
    simplify,
    solveset,
)
from sympy.core.symbol import _symbol
from sympy.core.relational import Relational, Equality
from sympy.core.expr import Expr
from sympy.core.function import expand
from sympy.core.add import Add
from sympy.solvers.inequalities import solve_univariate_inequality
from sympy.simplify.radsimp import fraction
from sympy.sets.sets import Set, FiniteSet
from sympy.functions.elementary.complexes import conjugate
from sympy.calculus.util import function_range

def to_side(expr: Relational, side="left") -> Relational:
    """This is a function for Relational, and change the Relational to Relation(Expr, 0, relation_str) or Relation(0, Expr, relation_str).

    Parameters
    ==========

    expr : Relational
    side : str, only "left" or "right", default "left"

    Return
    ======

    to_side : Relational

    Raises
    ======

    ValueError
        The side variable is only 'left' or 'right'.

    Examples
    ========

    >>> from sympy import *
    >>> x=symbols("x",real = True)
    >>> to_side(x < 3)
    x - 3 < 0
    >>> to_side(x < 3, side = "right")
    0 < 3 - x
    >>> expr = (x + 3) < (x + 2)
    >>> expr
    False
    """
    if isinstance(expr, Relational):
        if side == "left":
            return expr.func(Add(expr.lhs, -expr.rhs, evaluate=False), 0)
        elif side == "right":
            return expr.func(0, Add(expr.rhs, -expr.lhs, evaluate=False))
        else:
            raise ValueError("The side variable is only 'left' or 'right'.")

def function_transfer(f: Expr, x: Symbol, **kwargs) -> Function:
    """
    The transfer could be:
    scale,
    move,
    lift,
    other transfer will be ignored.
    """
    if kwargs:
        """

        # 好像多此一举了！

        # clear the functions that not has variate x.
        function_set = f.atoms(Function)
        function_list = []
        for fun in function_set:
            if(fun.args.has(x)):
                function_list.append(fun)

        """

        for k, v in kwargs.items():
            if k == "scale":
                f = f.subs(x, x / simplify(v))
            if k == "move":
                f = f.subs(x, x - v)
            if k == "lift":
                f = f + v
        return f

    else:
        raise ValueError("You should set the transfer names.")


def simplify_equation(eq: Equality, **kwargs) -> Equality:
    if kwargs.get("fraction") == True:
        lhs = fraction(eq.lhs)
        rhs = fraction(eq.rhs)
        eq = Eq(lhs[0] * rhs[1], lhs[1] * rhs[0])
    if kwargs.get("square") == True:
        eq = Eq(eq.lhs**2, eq.rhs**2)
    if kwargs.get("expr") == True:
        eq = eq.lhs - eq.rhs

    return eq


def abs_to_piecewise(f: Expr, x: Symbol, domain=Reals) -> Expr:
    abs_expr_set = f.atoms(Abs)
    zero_set = FiniteSet()
    for abs_expr in abs_expr_set:
        zero_set = zero_set + solveset(abs_expr, x, domain)

    if len(filter(lambda n: n.is_symbol, zero_set)) > 0:
        raise ValueError("存在无法判断大小的变量。")

    points = list(zero_set).sort()

    points_num = len(points)

    def is_positive(abs_expr, domain):
        if function_range(abs_expr, x, domain).inf >= 0:
            return abs_expr.args[0]
        else:
            return -abs_expr.args[0]

    for i in range(points_num):
        pass
